﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FirefliesClustering
{
    public class KMeans
    {

        private static string PrintListOfTuples(List<Tuple<float, float>> list)
        {
            Console.WriteLine("Centers that was found by fireflies algorithm.");

            string result = "\n";

            foreach (Tuple<float, float> vector in list)
            {
                result = result + "(" + vector.Item1 + "," + vector.Item2 + ")" + "\n";
            }

            return result;
        }

        public static List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>> DoTheClustering(Tuple<float,float>[] centroids, Tuple<float,float>[] vectors)
        {
            Console.WriteLine(PrintListOfTuples(centroids.ToList()));
            Tuple<float,float>[] centers = centroids;
            List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>> result = new List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>>();

            //Заполняем результат изначальными центрами и пустыми списками. Изначально в кластерах ни одного вектора.
            foreach (Tuple<float, float> center in centers)
            {
                result.Add(new Tuple<Tuple<float, float>, List<Tuple<float, float>>>(center, new List<Tuple<float, float>>()));
            }

            bool flag = true;

            while (flag)
            {

                // Для каждого вектора из изначального набора векторов.
                foreach (Tuple<float, float> vector in vectors)
                {
                    //Вычислим список пар, в котором первый элемент пары - номер кластер (номер центра), второй элемент пары - расстояние
                    //от вектора до этого центра.
                    List<Tuple<int, float>> centerNumberAndDistance = new List<Tuple<int, float>>();

                    int centerNumber = 0;
                    foreach (Tuple<float, float> center in centers)
                    {                        
                        centerNumberAndDistance.Add(new Tuple<int,float>(centerNumber, (float)Math.Sqrt(Math.Pow((vector.Item1-center.Item1),2)+Math.Pow((vector.Item2-center.Item2),2))));
                        centerNumber++;
                    }

                    //Находим, до какого из центров расстояние наименьшее.
                    int numberOfNearestCenterToVector = FindNearestCenter(centerNumberAndDistance);

                    // Добавляем в словарь: ключ - номер кластера, соответствующий номеру, который мы получили, значение - вектор, мы его определяем
                    // в этот кластер.

                    result = SmartInsert(result, numberOfNearestCenterToVector, vector);
                }

                //Пересчитываем центры
                for (int i = 0; i < centers.Length; i++) 
                {
                    Tuple<float,float> center = centers[i];

                    float sum1 = new float();
                    float sum2 = new float();
                    foreach (Tuple<float,float> vector in result[i].Item2)
                    {
                        sum1 = sum1 + vector.Item1;
                        sum2 = sum2 + vector.Item2;
                    }

                    float temp1 = (float)(1.0 / result[i].Item2.Count);
                    float temp2 = (float)(1.0 / result[i].Item2.Count);

                    float coor1 = (float)(1.0 / result[i].Item2.Count) * sum1;
                    float coor2 = (float)(1.0 / result[i].Item2.Count) * sum2;

                    center = new Tuple<float,float>((float)(1.0 / result[i].Item2.Count) * sum1, (float)(1.0 / result[i].Item2.Count) * sum2);

                    if (center.Item1 == centers[i].Item1 || center.Item2 == centers[i].Item2)
                    {
                        flag = false;
                    }

                    centers[i] = center;
                }

                //Заменяем центры в результате на пересчитанные
                for (int i = 0; i < result.Count; i++)
                {
                    List<Tuple<float,float>> tempList = result[i].Item2;
                    result[i] = new Tuple<Tuple<float, float>, List<Tuple<float, float>>>(centers[i], tempList);
                }

            }
            return result;
        }

        

        private static List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>> SmartInsert(List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>> result, int numberOfNearestCenterToVector, Tuple<float, float> vector)
        {
            List<Tuple<Tuple<float, float>, List<Tuple<float, float>>>> returningResult = result;

            bool flag = false;

            foreach (Tuple<float, float> v in returningResult[numberOfNearestCenterToVector].Item2)
            {
                if (v.Item1 == vector.Item1 && v.Item2 == vector.Item2)
                {
                    flag = true;
                    break;
                }
            }

            for (int i = 0; i < returningResult.Count; i++)
            {
                if (i != numberOfNearestCenterToVector)
                {
                    for (int j = 0; j < returningResult[i].Item2.Count; j++)
                    {

                        Tuple<float, float> v = returningResult[i].Item2[j];
                        if (v.Item1 == vector.Item1 && v.Item2 == vector.Item2)
                        {
                            returningResult[i].Item2.Remove(v);
                        }
                    }
                }
            }

            if (!flag)
            {
                returningResult[numberOfNearestCenterToVector].Item2.Add(vector);
                return returningResult;
            }
            else
            {
                return result;
            }
            

        }

        private static int FindNearestCenter(List<Tuple<int, float>> list)
        {
            int result = list.First().Item1;

            for (int i = 1; i < list.Count; i++)
            {
                if (list[result].Item2 > list[i].Item2)
                {
                    result = i;
                }
            }

            return result;
        }
    }
}
